//
// @Author: Zhenwei Xiao
// @Description:
// @Version: 1.0.0
// @Date: 2021/3/29 9:59 上午
// @Copyright: MIN-Group；国家重大科技基础设施——未来网络北大实验室；深圳市信息论与未来网络重点实验室
//

const identity = require("../minsecurity/identity/identity")
const defaultIdentifyName = "/localhost/operator"
const signature = require("../component/Signature")
const safebag = require("./SafeBag")
const identifymanager = require ("./IdentifyManager")

//
// 用于给网络包签名和验签
//
// @Description:
//	1. 请调用 CreateKeyChain 方法创建一个 KeyChain 指针，或者创建一个 KeyChain 结构体后，手动调用 InitialKeyChain 进行初始化
//
class KeyChain {
    constructor(IdentifyManager, currentIdentity)
    {
        this.IdentifyManager = IdentifyManager
        this.currentIdentity = currentIdentity
    }
	//IdentifyManager                    // 内嵌身份管理器
	//currentIdentity *identity.Identity // 当前使用的身份

    //
    // 初始化 KeyChain
    //
    // @Description:
    // @receiver k
    // @return error
    //
    InitialKeyChain() {
        this.IdentifyManager = new identifymanager.IdentifyManager
        var err = this.IdentifyManager.Init()
        // 初始化 IdentifyManager
        if (err != null) {
            return err
        }

        // 指定当前使用默认的网络身份
        this.currentIdentity = this.IdentifyManager.defaultIdentity

        // TODO: 考虑是否需要在没有默认身份的时候创建一个缺省的本地网络身份
        //// 如果没有默认的身份，则创建一个缺省的默认身份
        //if k.currentIdentity == nil {
        //	// 创建一个默认的不加密的网络身份
        //	newIdentity, err := k.IdentifyManager.CreateIdentityByName(defaultIdentifyName, "")
        //	if err != nil {
        //		return nil, err
        //	}
        //	k.IdentifyManager.SetDefaultIdentity(newIdentity)
        //	k.currentIdentity = newIdentity
        //}

        return null
    }

    //
    // 设置当前使用的网络身份
    //
    // @Description:
    //	1.
    // @receiver k
    // @param identity *identity.Identity
    // @param password string
    // @return error
    //
    SetCurrentIdentity(identity , password) {
        // 这边用 password 对目标网络身份进行解锁，调用 identity.UnLock(password)
        var res = identity.UnLock(password, this.IdentifyManager.privateKeyEncryptionAlgorithm)
        if (res[1] != null) {
            return res[1]
        }
        this.currentIdentity = identity
        return null
    }

    //
    // 为一个网络身份申请证书
    //
    // @Description:
    // @receiver k
    // @param identity *identity.Identity
    // @param force bool
    // @return error
    //
    GenerateCertificationForIdentity(identity, force) {
        // TODO: 这边应该发起网络通信，向 MIS 请求给这个网络身份签发一个证书，留待 MIR 完成后进行补充
        return null
    }

    //
    // 给一个通用的网络包进行签名
    //
    // @Description:
    // @receiver k
    // @param packet  *packet.MINPacket
    // @return error
    //
    Sign(packet) {
        // 首先检查当前使用的身份是否可以用来签名
        console.log("currentid is ", this.currentIdentity)
        var err = checkIdentifyCanUseToSign(this.currentIdentity)
        if (err != null) {
            return err
        }

        // 提取标识区和只读区的值，对其进行签名
        var rawData = getIdentifierAndReadOnlyValueFromPacket(packet)
        if (rawData[1] != null) {
            return rawData[1]
        }
        const uti = require("../minsecurity/crypto/utils")
        console.log("rawData Sign?" , [...rawData[0]])
        console.log("rawData2str Sign?" , uti.Bytes2Str([...rawData[0]]))
        // 对标识区和只读区进行签名
        var signResult = this.currentIdentity.Sign([...rawData[0]], null)
        if (signResult[1] != null) {
            return signResult[1]
        }
        console.log("signResult", signResult[0])
        // 往包中添加签名
        var sig = signature.createSignatureByIdentifyAndValue(this.currentIdentity, signResult[0])
        // if (sig[1] != null) {
        //     return sig[1]
        // }

        // TODO: 这边直接给通过 addSignature 的方式存在问题，应该定义标识区的固定位置存储特定含义的标识
        packet.SignatureField.addSignature(sig)
        return null
    }

    //
    // 对 CPacket 进行签名
    //
    // @Description:
    // @receiver k
    // @param cPacket  *packet.CPacket
    //
    SignCPacket(cPacket) {
        var err = cPacket.fillDataToFields()
        if (err != null) {
            return err
        }
        console.log("signfilldata??", cPacket)
        return this.Sign(cPacket.MINPacket)
    }

    //
    // 对 Interest 进行签名
    //
    // @Description:
    // @receiver k
    // @param interest *packet.Interest
    // @return error
    //
    SignInterest(interest) {
        var err = interest.fillDataToFields()
        if  (err != null) {
            return err
        }
        return this.Sign(interest.MINPacket)
    }

    //
    // 对 Data 进行签名
    //
    // @Description:
    // @receiver k
    // @param data  *packet.Data
    // @return error
    //
    SignData(data) {
        var err = data.fillDataToFields()
        if  (err != null) {
            return err
        }
        return this.Sign(data.MINPacket)
    }

    //
    // 验证一个MIN网络包中的签名是否有效
    //
    // @Description:
    // @receiver k
    // @param signature  *packet.MINPacket
    // @return error
    //
    Verify(minPacket) {
        // 提取签名区的第一个签名进行验证（认为签名区的第一个签名为包的签名，包含标识区和只读区签名）
        var signature = minPacket.SignatureField.getSignature(0)
        console.log("验证提取的signature", signature)

        // 首先提取 signature 中签名者的网络身份信息，并根据它在本地查询是否有对应的网络身份
        var identifyName = signature.SigInfo.getKeyLocator().getIdentifier().toUri()
        console.log("identifyName", identifyName)
        identifyName = identifyName.substring(1, identifyName.length)
        var verifyIdentity = new identity.Identity()
        verifyIdentity = this.IdentifyManager.GetIdentifyByName(identifyName)

        // 如果在本地没有找到对应的网络身份，则验签失败
        if (verifyIdentity === undefined) {
            return new KeyChainError("Verify failed, could not found the identity, which name is " + identifyName)
        }

        // 提取标识区和只读区的值，使用其进行验签
        var rawData = getIdentifierAndReadOnlyValueFromPacket(minPacket)
        rawData[0] = [...rawData[0]]
        console.log("rawData[0]", rawData[0])
        // 验证包的签名是否合法
        console.log("signature.SigValue.getValue()", signature.SigValue.getValue())
        const uti = require("../minsecurity/crypto/utils")
        console.log("rawData[0]tostr", uti.Bytes2Str(rawData[0]))
        var err = verifyIdentity.Verify(rawData[0], signature.SigValue.getValue(),null)
        if (err[1] != null) {
            console.log("身份认证失败！！！！")
            return err[1]
        }
        console.log("身份验证结果", err[0])


        // 没有发生任何错误说明验证成功
        return null
    }

    //
    // 验证一个 CPacket 中的签名是否有效
    //
    // @Description:
    // @receiver k
    // @param icPacket  *packet.CPacket
    // @return error
    //
    VerifyCPacket(icPacket) {
        var err = icPacket.fillDataToFields()
        if (err != null) {
            return err
        }
        console.log("icPacke", icPacket)
        return this.Verify(icPacket.MINPacket)
    }

    //
    // 验证一个 Interest 中的签名是否有效
    //
    // @Description:
    // @receiver k
    // @param interest  *packet.Interest
    // @return bool
    //
    VerifyInterest(interest) {
        var err = interest.fillDataToFields()
        if (err != null) {
            return err
        }
        return this.Verify(interest.MINPacket)
    }

    //
    // 验证一个 Data 中的签名是否有效
    //
    // @Description:
    // @receiver k
    // @param data  *packet.Data
    // @return bool
    //
    VerifyData(data) {
        var err = data.fillDataToFields()
        if  (err != null) {
            return err
        }
        return this.Verify(data.MINPacket)
    }
    //
    // 从一个 SafeBag 中导入网络身份，保存到本地
    //
    // @Description:
    // @receiver k
    // @param bag *SafeBag
    // @param password string
    // @param force bool		是否强制导入，如果是，则本地存在同名网络身份的情况下会覆盖原有的网络身份
    //
    ImportSafeBag(bag, password, force) {
        // 加载一个本地的网络身份
        var newIdentity = new identity.Identity
        var err = newIdentity.Load(bag.Value, password);
        if (err != null) {
            return err
        }

        // 如果当前网络身份不存在，或者存在但是指定了 force = true，则将导入的网络身份进行持久化存储
        if (!this.IdentifyManager.ExistIdentity(newIdentity.Name) || force) {
            var err = this.IdentifyManager.SaveIdentify(newIdentity, force)
            if (err != null) {
                return err
            }
            return null
        } else {
            // 如果网络身份存在，并且没有指定强制覆盖，则导入失败
            return new KeyChainError("Identify "+ newIdentity.Name + " is already exists!")
        }
    }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
///// 构造函数 Create*
/////////////////////////////////////////////////////////////////////////////////////////////////////////

//
// 创建并初始化一个KeyChain对象
//
// @Description:
// @return *KeyChain
// @return error
//
function CreateKeyChain() {
	var keyChain = new KeyChain
	return [keyChain, keyChain.InitialKeyChain()]
}

//
// 检查一个网络身份是否可用
//
// @Description:
//	1. 首先检查 identity 是否为空；
//	2. 接着检查 identity 是否包含私钥；
//	3. 接着检查 identify 是否被锁定
// @param identity *identity.Identity
// @return error
//
function checkIdentifyCanUseToSign(identity) {
	// 首先检查 identity 是否为空
	if (identity == null) {
		return new KeyChainError("Identity is nil!")
	}

	// 检查是否存在私钥（如果该身份只是用来验签的，则很可能只包含公钥和证书）
	if (!identity.HashPrivateKey()) {
		return new KeyChainError("Identify not have Private key, so can't use to sign!")
	}

	// 检查秘钥是否已经解锁，如果处于被锁定的状态，则不能用于签名
	if (identity.IsLocked()) {
		return new KeyChainError("Identify is locked, so can't use to sign")
	}

	// 返回空表示通过验证，可以用来签名
	return null
}

//
// 从 MIN 网络包中提取出标识区和只读区的值，用于签名和验签
//
// @Description:
// @param packet  *packet.MINPacket
// @return []byte, error
//
function getIdentifierAndReadOnlyValueFromPacket(packet) {
	// 将标识区和只读区的 Value 读取出来进行签名
	var identifierBlock = packet.IdentifierField.selfWireEncode(packet.IdentifierField)
    console.log("identifierBlock", identifierBlock)
	// if (identifierBlock[1] != null) {
	// 	return [null, identifierBlock[1]]
	// }
	var readOnlyBlock = packet.ReadOnlyField.selfWireEncode(packet.ReadOnlyField)
    console.log("readOnlyBlock", readOnlyBlock)
	// if (readOnlyBlock[1] != null) {
	// 	return [null, readOnlyBlock[1]]
	// }

	// var rawData = new Buffer.alloc(identifierBlock[0].GetValue().length + readOnlyBlock[0].GetValue().length)
    //var rawData = new Buffer.alloc(identifierBlock.getValue().byteLength + readOnlyBlock.getValue().byteLength)
    var rawData = Buffer.alloc(identifierBlock.getValue().byteLength + readOnlyBlock.getValue().byteLength)
    let buf1 = new Buffer(identifierBlock.getValue())
    let buf2 = new Buffer(readOnlyBlock.getValue())
    rawData.set(buf1, 0)
    rawData.set(buf2, buf1.length)
    //identifierBlock.getValue().copy(rawData)
    //readOnlyBlock.getValue().copy(rawData, identifierBlock.getValue().byteLength)
	//copy(rawData[len(identifierBlock[0].GetValue()):], readOnlyBlock[0].GetValue())
	return [rawData, null]
}



//
// 将一个网络身份导出为一个 SafeBag 对象
//
// @Description:
// @param identity *identity.Identity
// @param password string	用于对导出的网络身份整体进行加密
// @return *SafeBag
// @return error
//
function ExportSafeBag(identity, password) {
	var res = identity.Dump(password)
	if (res[1] != null) {
		return [null, res[1]]
	}
	var safeBag = new safebag.SafeBag(res[0])
	return [safeBag, res[1]]
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
///// 错误处理
/////////////////////////////////////////////////////////////////////////////////////////////////////////

class KeyChainError {
	constructor(msg){
        this.msg = msg
    }
    Error() {
        return new Error("KeyChainError: " + this.msg)
    }
}

module.exports = {
    KeyChain: KeyChain,
    CreateKeyChain: CreateKeyChain,
	ExportSafeBag: ExportSafeBag
}